// Running code:
// =============
// node mult.js <operand1> <operand2>
// e.g.
// node mult.js 12 -12
// replace node with nodemon for watching modification

const prep = (num, width, { desc, neg = 0, padTimes = 1 } = {}) => {
	const signedBin = num >= 0
		? parseInt(num).toString(2).padStart(width, 0)
		: parseInt((1 << width) - Math.abs(num)).toString(2)

	const res = signedBin
		.padStart(padTimes * width, signedBin[0])
		.split('')
		.map(i => parseInt(i))
		.map(i => neg ? 1 - i : i)
		.reverse()

	console.log(`${desc}: ${res.slice().reverse().join('')}`)

	return res
}

const fullAdderAccum = (accum, adder, carry, { calcWidth, onBit, retainWidth } = {}) => {


	const P = accum;
	const M = adder;
	const C = carry;
	const r = onBit;

	// Simulating a full adder
	for (let i = 0; i < calcWidth; i++) {
		const p = M[i] ^ P[i + r] ^ C[i + r]
		const c = (M[i] & P[i + r]) | (P[i + r] & C[i + r]) | (M[i] & C[i + r])
		P[i + r] = p;
		C[i + r + 1] = c;
	}
	// console.log('FA', accum, adder, calcWidth, onBit)


	P.splice(retainWidth);
	C.fill(0)
}

const naive = function (Morg, Qorg, width = 8) {
	const M = prep(Morg, width, { desc: 'M', padTimes: 2 })
	const Q = prep(Qorg, width, { desc: 'Q', padTimes: 2 })

	const negM = M.at(-1);
	const negQ = Q.at(-1);

	const C = Array(width * 2 + 1).fill(0);	// for carry
	const P = Array(width * 2).fill(0);			// for product

	let qBit = 0;
	while (qBit < width * 2) {

		if (Q[qBit]) {
			fullAdderAccum(P, M, C, { calcWidth: width * 2, onBit: qBit, retainWidth: width * 2 });
		}
		console.log('P ', P.slice().reverse().join(''))
		qBit++;
	}

	const res = parseInt(P.slice(0, 2 * width).reverse().join(''), 2)
	// return res;
	return (negM ^ negQ) ? res - (1 << 2 * width) : res
}


const booth = function (Morg, Qorg, width = 8) {
	const M = prep(Morg, width, { desc: 'M ' })
	const Q = prep(Qorg, width, { desc: 'Q ' })

	const negM = M.at(-1);
	const negQ = Q.at(-1);

	// first get negative M (by 2's comp)
	const M_ = prep(Morg - 1, width, { desc: 'M_', neg: true });

	// product register, extended by 1 bit
	const P = Array(2 * width + 1).fill(0);

	// carry array always have 1 bit longer than prod
	const C = Array(2 * width + 2).fill(0);

	// Initialize M, by assigning multiplier to the [1:width] bits
	// Note we have converted number to array, hence LSB on the head
	for (let i = 0; i < width; i++) {
		P[i + 1] = Q[i];
	}

	// the main loop. Iterating over all bits 
	for (let i = 0; i < width; i++) {
		switch (P.slice(0, 2).reverse().join('')) {
			case '10':
				fullAdderAccum(P, M_, C, { calcWidth: width, onBit: width + 1, retainWidth: width * 2 + 1 });
				break;
			case '01':
				fullAdderAccum(P, M, C, { calcWidth: width, onBit: width + 1, retainWidth: width * 2 + 1 });
				break;
			default:
				break;
		}

		// Arithmetic shift by duplicating original MSB
		P.push(P.at(-1))
		P.shift()
		console.log('P  ', P.slice().reverse().join(''))
	}

	const res = parseInt(P.slice(1).reverse().join(''), 2)

	return (negM ^ negQ) ? res - (1 << 2 * width) : res
}

const [M, Q, W = 8] = process.argv.slice(2);
console.log(naive(M, Q, parseInt(W)))
console.log(booth(M, Q, parseInt(W)))